#ifndef MSGQUEUE_HPP
#define MSGQUEUE_HPP

#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PATH_NAME "."
#define PROJ_ID 1
#define MODE_DEFAULT 0666
#define DATA_SIZE 1024
#define EXIT_ERROR(m) \
    do                \
    {                 \
        perror(m);    \
        exit(1);      \
    } while (0)

class Msgqueue
{
    struct Msgbuf
    {
        long mtype;
        char mtext[DATA_SIZE];
    };

protected:
    Msgqueue()
    {
        _key = ftok(PATH_NAME, PROJ_ID);
        std::cout << "Msgqueue() key:0x" << std::hex << _key << std::endl;
    }

    ~Msgqueue()
    {
    }

    void Getmsqid(const int msgflg)
    {
        _msqid = msgget(_key, msgflg);
        if (_msqid < 0)
        {
            EXIT_ERROR("msgget");
        }
        std::cout << "msgget success! msqid:" << _msqid << std::endl;
    }

    void Destroy()
    {
        int ret = msgctl(_msqid, IPC_RMID, nullptr);
        if (ret < 0)
        {
            EXIT_ERROR("msgctl");
        }
        std::cout << "msgrm success! msqid:" << _msqid << std::endl;
    }

    void Send(const std::string &msg, const long type)
    {
        Msgbuf buf;
        buf.mtype = type;
        strcpy(buf.mtext, msg.c_str());
        int ret = msgsnd(_msqid, &buf, DATA_SIZE, 0);
        if (ret < 0)
        {
            EXIT_ERROR("msgsnd");
        }
    }

    void Receive(std::string &msg, const long type)
    {
        Msgbuf buf;
        ssize_t n = msgrcv(_msqid, &buf, DATA_SIZE, type, 0);
        if (n < 0)
        {
            EXIT_ERROR("msgrcv");
        }
        buf.mtext[n] = 0;
        msg = buf.mtext;
    }

    void Stat()
    {
        struct msqid_ds ms;
        int ret = msgctl(_msqid, IPC_STAT, &ms);
        std::cout << "Stat key:0x" << std::hex << ms.msg_perm.__key << std::endl;
    }

protected:
    int _msqid;
    key_t _key;
};

const long server_type = 1;
const long client_type = 2;

class Server : public Msgqueue
{
public:
    Server()
    {
        Msgqueue::Getmsqid(IPC_CREAT | IPC_EXCL | MODE_DEFAULT);
        Stat();
    }

    ~Server()
    {
        Msgqueue::Destroy();
    }

    void Receive(std::string &msg)
    {
        Msgqueue::Receive(msg, client_type);
    }
};

class Client : public Msgqueue
{
public:
    Client()
    {
        Getmsqid(IPC_CREAT);
        Stat();
    }
    ~Client()
    {
    }

    void Send(const std::string &msg)
    {
        Msgqueue::Send(msg, client_type);
    }
};

#endif